import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_LINE_LOOP;
import static org.lwjgl.opengl.GL11.GL_LINE_STRIP;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW_MATRIX;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glColor3f;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glFlush;
import static org.lwjgl.opengl.GL11.glFrustum;
import static org.lwjgl.opengl.GL11.glGetDouble;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glRotatef;
import static org.lwjgl.opengl.GL11.glTranslated;
import static org.lwjgl.opengl.GL11.glVertex3d;
import static org.lwjgl.opengl.GL11.glVertex3f;
import static org.lwjgl.opengl.GL11.glViewport;

import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.util.ArrayList;

import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;


public class GLSwingTest extends Thread {
	static final float gravity = 9.8f;
	static float[] red = { 1, 0, 0 };
	static float[] blue = { 0, 0, 1 };
	static float[] green = { 0, 1, 0 };
	static float[] yellow = { 1, 1, 0 };
	static ByteBuffer shitState = ByteBuffer.allocateDirect(128);
	static DoubleBuffer lastState = shitState.asDoubleBuffer();
	static float n = 0;
	static double piover180 = Math.PI / 180;

	static ArrayList<int[]> thePath = new ArrayList<int[]>();

	static double scalex, scaley, X, Y, Z;
	static float pitch, yaw, roll;

	// None of these 'RotateLocalYaw/Pitch/Roll' functions work. Ignore them.
	static void rotateLocalYaw(int whichway)
	{
		if (whichway > 0)
		{
			pitch += (float) Math.sin(piover180 * roll);
			yaw += (float) Math.cos(piover180 * pitch);
			roll += (float) Math.sin(piover180 * pitch);
		} else
		{
			pitch -= (float) Math.sin(piover180 * roll);
			yaw -= (float) Math.cos(piover180 * pitch);
			roll -= (float) Math.sin(piover180 * pitch);
		}
		// glRotatef(angle, (float)Math.sin(piover180*roll) , (float)
		// Math.cos(piover180*pitch) ,(float) Math.sin(piover180*pitch));
	}

	static void rotateLocalPitch(int whichway)
	{
		if (whichway > 0)
		{
			pitch += (float) Math.cos(piover180 * yaw);
			yaw += (float) Math.sin(piover180 * roll);
			roll += (float) Math.sin(piover180 * yaw);
		} else
		{
			pitch -= (float) Math.cos(piover180 * yaw);
			yaw -= (float) Math.sin(piover180 * roll);
			roll -= (float) Math.sin(piover180 * yaw);
		}
		// glRotatef(angle, (float)Math.cos(piover180*yaw) , (float)
		// Math.sin(piover180*roll) ,(float) Math.sin(piover180*yaw));

	}

	static void rotateLocalRoll(int w)
	{
		if (w > 0)
		{
			pitch += (float) Math.sin(piover180 * yaw) * -1;
			yaw += (float) Math.sin(piover180 * pitch);
			roll += (float) Math.cos(piover180 * yaw);
		} else
		{
			pitch -= (float) Math.sin(piover180 * yaw) * -1;
			yaw -= (float) Math.sin(piover180 * pitch);
			roll -= (float) Math.cos(piover180 * yaw);
		}
		// glRotatef(angle, (float)Math.sin(piover180*yaw)*-1 , (float)
		// Math.sin(piover180*pitch) ,(float) Math.cos(piover180*yaw));
	}

//	public static void main(String[] args)
//	{
//		GUI gui = new GUI();
//		
//
//
//	}
	
	public void run() {
		try
		{

			Display.setDisplayModeAndFullscreen(new DisplayMode(1280, 1024));
			Display.create();

		} catch (Exception e)
		{
		}

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glFrustum(-1.0, 1.0, -1.0, 1.0, 1.5, 200.0);

		glMatrixMode(GL_MODELVIEW);
		glViewport(0, 0, 1280, 1024);
		
		while (!Display.isCloseRequested())
		{

			try
			{
				display();
			} catch (Exception e)
			{
				e.printStackTrace();
				System.exit(1);
			}

			// You see this line?
			Display.update();
			// Don't you DARE remove it!
			// Doing so will lock up the entire machine!

		}
		Display.destroy();
	}
	
	static void closeWindow() {
		Display.destroy();
	}
	
	// This is unimportant
	static double[] buildPath()
	{
		double[] path = null;
		double piover180 = Math.PI / 180;

		for (int i = 0; i < path.length; i++)
		{
			path[i] = path[i] / (i / 36000.0);
		}
		return path;
	}

	// So is this
	static void drawSomeStuff(double[] path)
	{
		glColor3f(.6f, 0, 0);
		glBegin(GL_LINE_STRIP);
		for (int i = 0; i < path.length; i++)
		{
			glVertex3d(0, path[i], i / 1000.0);
		}
		glEnd();

	}
	
	static void moveForwardButtonTest() {
		Z -= .1 * Math.cos(piover180 * yaw);
		Y -= .1 * Math.sin(piover180 * pitch);
		X += .1 * Math.sin(piover180 * yaw);
	}

	/*
	 * This will create 4 cubes of different colors surrounding the camera. It
	 * properly handles the movement of the camera in the XZ plane, that is, by
	 * only using the arrow keys. You can change pitch with the 1 and 4 keys,
	 * and you can change roll with the X and Z keys. But doing this will
	 * severely fuck everything up from then on out. Sorry.
	 */
	static void display() throws InterruptedException
	{
		glMatrixMode(GL_MODELVIEW);
		glClear(GL_COLOR_BUFFER_BIT);
		glLoadIdentity();

		if (Mouse.isButtonDown(0))
		{
			X = (Mouse.getX() - Display.getDisplayMode().getWidth() / 2.0) / 100.0;
			Y = (Mouse.getY() - Display.getDisplayMode().getWidth() / 2.0) / 100.0;

		}
		if (Mouse.isButtonDown(2))
		{
			scalex = Mouse.getX() / 200.0;
			scaley = Mouse.getY() / 200.0;
		}

		// Does not handle roll!
		if (Keyboard.isKeyDown(Keyboard.KEY_UP))
		{

			Z += .1 * Math.cos(piover180 * yaw);
			Y += .1 * Math.sin(piover180 * pitch);
			X -= .1 * Math.sin(piover180 * yaw);

		}
		// Does not handle roll!
		if (Keyboard.isKeyDown(Keyboard.KEY_DOWN))
		{
			Z -= .1 * Math.cos(piover180 * yaw);
			Y -= .1 * Math.sin(piover180 * pitch);
			X += .1 * Math.sin(piover180 * yaw);

		}

		glGetDouble(GL_MODELVIEW_MATRIX, lastState);

		if (Keyboard.isKeyDown(Keyboard.KEY_RIGHT))
		{
			yaw++;
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_LEFT))
		{
			yaw--;
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_1))
		{
			pitch++;
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_4))
		{
			pitch--;
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_X))
		{
			roll += 1;
		}
		if (Keyboard.isKeyDown(Keyboard.KEY_Z))
		{
			roll -= 1;
		}

		glRotatef(pitch, 1, 0, 0);
		glRotatef(yaw, 0, 1, 0);
		glRotatef(roll, 0, 0, 1);

		
		drawCubed(X,Y,Z,red);
		
		glTranslated(X, Y, Z);

		drawCubed(X,Y,Z,blue);
		// This is important otherwise your motion is too fast.
		Thread.sleep(10);

		
		
		drawCube(0, 0, -4, red);
		drawCube(4, 0, 0, blue);
		drawCube(0, 0, 4, green);
		drawCube(-4, 0, 0, yellow);

		glFlush();
	}

	// This will draw a wireframe cube of a fixed size at the location (x,y,z)
	// with color {red, green, blue}
	static void drawCubed(double x, double y, double z, float[] color)
	{
		glColor3f(color[0], color[1], color[2]);
		glBegin(GL_LINE_LOOP);
		glVertex3d(0 + x, 0 + y, 0 + z);
		glVertex3d(1 + x, 0 + y, 0 + z);
		glVertex3d(1 + x, 1 + y, 0 + z);
		glVertex3d(0 + x, 1 + y, 0 + z);

		glVertex3d(0 + x, 1 + y, -1 + z);
		glVertex3d(0 + x, 0 + y, -1 + z);
		glVertex3d(1 + x, 0 + y, -1 + z);
		glVertex3d(1 + x, 1 + y, -1 + z);
		glVertex3d(0 + x, 1 + y, -1 + z);

		glVertex3d(0 + x, 1 + y, 0 + z);
		glVertex3d(0 + x, 0 + y, 0 + z);
		glVertex3d(0 + x, 0 + y, -1 + z);
		glVertex3d(1 + x, 0 + y, -1 + z);
		glVertex3d(1 + x, 0 + y, 0 + z);
		glVertex3d(1 + x, 1 + y, 0 + z);
		glVertex3d(1 + x, 1 + y, -1 + z);
		glEnd();
	}
	
	static void drawCube(float x, float y, float z, float[] color)
	{
		glColor3f(color[0], color[1], color[2]);
		glBegin(GL_LINE_LOOP);
		glVertex3f(0 + x, 0 + y, 0 + z);
		glVertex3f(1 + x, 0 + y, 0 + z);
		glVertex3f(1 + x, 1 + y, 0 + z);
		glVertex3f(0 + x, 1 + y, 0 + z);

		glVertex3f(0 + x, 1 + y, -1 + z);
		glVertex3f(0 + x, 0 + y, -1 + z);
		glVertex3f(1 + x, 0 + y, -1 + z);
		glVertex3f(1 + x, 1 + y, -1 + z);
		glVertex3f(0 + x, 1 + y, -1 + z);

		glVertex3f(0 + x, 1 + y, 0 + z);
		glVertex3f(0 + x, 0 + y, 0 + z);
		glVertex3f(0 + x, 0 + y, -1 + z);
		glVertex3f(1 + x, 0 + y, -1 + z);
		glVertex3f(1 + x, 0 + y, 0 + z);
		glVertex3f(1 + x, 1 + y, 0 + z);
		glVertex3f(1 + x, 1 + y, -1 + z);
		glEnd();

	}

	
	/*
	 * This is an example of some code to handle keyboard and mouse input.
	 */
	public static void pollInput()
	{

		if (Mouse.isButtonDown(0))
		{
			int x = Mouse.getX();
			int y = Mouse.getY();

			System.out.println("MOUSE DOWN @ X: " + x + " Y: " + y);
		}

		if (Keyboard.isKeyDown(Keyboard.KEY_SPACE))
		{
			System.out.println("SPACE KEY IS DOWN");
		}

		while (Keyboard.next())
		{
			if (Keyboard.getEventKeyState())
			{
				if (Keyboard.getEventKey() == Keyboard.KEY_A)
				{
					System.out.println("A Key Pressed");
				}
				if (Keyboard.getEventKey() == Keyboard.KEY_S)
				{
					System.out.println("S Key Pressed");
				}
				if (Keyboard.getEventKey() == Keyboard.KEY_D)
				{
					System.out.println("D Key Pressed");
				}
			} else
			{
				if (Keyboard.getEventKey() == Keyboard.KEY_A)
				{
					System.out.println("A Key Released");
				}
				if (Keyboard.getEventKey() == Keyboard.KEY_S)
				{
					System.out.println("S Key Released");
				}
				if (Keyboard.getEventKey() == Keyboard.KEY_D)
				{
					System.out.println("D Key Released");
				}
			}
		}
	}

	
	
	
	
	class Cart
	{
		
	}

	
	
	
	
	/*
	 * Down here is some junk code. I thought ahead of time that it may improve
	 * performance if instead of computing trig functions over and over every
	 * time we need to know the angle of the track upon which the cart sits, we
	 * could just look the values up in huge tables. This probably won't be
	 * necessary, though.
	 */

	public static double[] buildSineTable()
	{
		double piover180 = (Math.PI / 180.0);
		double[] sineTable = new double[36000];
		for (double degree = 0; degree < 360; degree += 0.01)
		{
			int intdegree = (int) (degree * 100.0);

			if (intdegree == 9000 || intdegree == 27000)
			{
				sineTable[intdegree] = 1.0;
			} else if (intdegree == 18000 || intdegree == 0.0)
			{
				sineTable[intdegree] = 0;
			} else
			{
				sineTable[intdegree] = Math.sin(piover180 * degree);
			}
		}
		return sineTable;
	}

	public static double[] buildCosineTable()
	{
		double piover180 = (Math.PI / 180.0);
		double[] cosineTable = new double[36000];
		for (double degree = 0; degree < 360; degree += 0.01)
		{
			int intdegree = (int) (degree * 100.0);

			if (intdegree == 9000 || intdegree == 27000)
			{
				cosineTable[intdegree] = 0;
			} else if (intdegree == 18000 || intdegree == 0.0)
			{
				cosineTable[intdegree] = 1.0;
			} else
			{
				cosineTable[intdegree] = Math.cos(piover180 * degree);
			}
		}
		return cosineTable;
	}

	/*
	 * This is probably a good idea. Use a local spherical coordinate system for
	 * the cart, and then convert it to the absolute cartesian system that
	 * OpenGL uses. I haven't tried to implement this yet.
	 */
	static class Spherical
	{

		double[] convertToCartesian(double r, double phi, double theta)
		{
			return null;

		}

		double[] convertToCartesian(double[] sphericalVector)
		{
			return null;
		}
	}
}
